package unify.gui;

/*
 * Copyright (c) 1995, 2008, Oracle and/or its affiliates. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   - Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *   - Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 *   - Neither the name of Oracle or the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
/*
 * TrayIconDemo.java
 */

import java.awt.*;
import java.awt.event.*;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Enumeration;

import java.util.logging.LogManager;
import java.util.logging.Logger;
import javax.swing.*;

import unify.ShowLibrary;
import unify.data.Episode;
import unify.data.Search;
import unify.data.Season;
import unify.data.Settings;
import unify.data.Show;


public class SysTrayIcon {
	private static SysTrayIcon _instance = null;
	final PopupMenu popup;
	public final TrayIcon trayIcon;
	final SystemTray tray;
	private ShowLibrary showLib;
	private Settings settings;
	private final static Logger LOGGER = Logger.getLogger(SysTrayIcon.class.getName());

	private SysTrayIcon() {
		this.popup = new PopupMenu();
		this.tray = SystemTray.getSystemTray();
		this.showLib = ShowLibrary.getInstance();
		this.settings = Settings.getInstance();
		this.trayIcon = new TrayIcon(Settings.getInstance().getIconImage());
	}

	public static synchronized SysTrayIcon getInstance() {
		if (_instance == null) {
			_instance = new SysTrayIcon();
		}
		return _instance;
	}

	public void RunIcon() {
		/* Use an appropriate Look and Feel */
		try {
			UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
			// UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel");
		} catch (UnsupportedLookAndFeelException ex) {
			ex.printStackTrace();
		} catch (IllegalAccessException ex) {
			ex.printStackTrace();
		} catch (InstantiationException ex) {
			ex.printStackTrace();
		} catch (ClassNotFoundException ex) {
			ex.printStackTrace();
		}
		/* Turn off metal's use of bold fonts */
		UIManager.put("swing.boldMetal", Boolean.FALSE);
		// Schedule a job for the event-dispatching thread:
		// adding TrayIcon.
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				createAndShowGUI();
			}
		});
	}

	private void createAndShowGUI() {
		// Check the SystemTray support
		if (!SystemTray.isSupported()) {
			LOGGER.severe("SystemTray is not supported");
			System.exit(1);
			return;
		}

		generatePopup();

		try {
			tray.add(trayIcon);
		} catch (AWTException e) {
			LOGGER.severe("TrayIcon could not be added.");
			System.exit(1);
			return;
		}

		trayIcon.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				GUI.main();
			}
		});
	}

	/**
	 * Generates the popup, first erasing all children.
	 * 
	 */
	public void generatePopup() {
		popup.removeAll();

		final MenuItem aboutItem = new MenuItem("About");
		final MenuItem checkFeedsItem = new MenuItem("Check Feeds Now");
		final MenuItem exitItem = new MenuItem("Exit");
		final Menu onToday = new Menu("On Today");
		final Menu onTomorrow = new Menu("On Tomorrow");
		final Menu allShows = new Menu("Available");
		final Menu newShows = new Menu("New Shows");

		createMenu(onToday, showLib.onToday);
		createMenu(onTomorrow, showLib.onTomorrow);
		createAllShowsMenu(allShows);
		createNewShowsMenu(newShows);

		aboutItem.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				String aboutMe = "An internet TV unification program, runs off RSS feeds.  Designed and coded by Eruza.";
				String myDelay = "Delay currently set to: " + settings.getDelay() + " minutes.";
				String myFNS = "Currently find new shows is ";
				if(settings.isFindNewShows()) {
					myFNS = myFNS + "on.";
				}
				else {
					myFNS = myFNS + "off.";
				}
				LogManager manager = LogManager.getLogManager();
				Enumeration<String> loggers = manager.getLoggerNames();
				for (; loggers.hasMoreElements();) {
					System.out.println((String) loggers.nextElement());
				}
				JOptionPane.showMessageDialog(null,
						aboutMe + "\n" + myDelay + "\n" + myFNS);
			}
		});

		checkFeedsItem.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				ShowLibrary.getInstance().checkFeedsNow();
			}
		});

		exitItem.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				exit(0);
			}
		});

		if(allShows.getItemCount()==0) {
			MenuItem noShows = new MenuItem("No shows! Click to add more!");
			noShows.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					GUI.main();
				}
			});
			allShows.add(noShows);
		}
		if(onToday.getItemCount()==0) {
			onToday.add("No shows on today!");
		}
		if(onTomorrow.getItemCount()==0) {
			onTomorrow.add("No shows on tomorrow!");
		}

		popup.add(aboutItem);
		popup.add(checkFeedsItem);
		popup.addSeparator();
		popup.add(onToday);
		popup.add(onTomorrow);
		popup.add(allShows);
		if(newShows.getItemCount()>0) {
			popup.add(newShows);
		}
		popup.addSeparator();
		popup.add(exitItem);

		trayIcon.setPopupMenu(popup);
	}

	private void createMenu(Menu menu, ArrayList<Show> shows) {
		for(int i=0;i<shows.size();i++) {
			Show show = shows.get(i);
			Season season = show.getSeason();
			for(Episode episode : season.getEpisodes()) {
				final Menu showMenu = new Menu(show.getTitle() + " (" + season.getNumber() + "x" + episode.getNumber() + ")");
				menu.add(showMenu);
				final MenuItem linkItem = new MenuItem(episode.getLinkLabel());				
				final URI myURL = URI.create(episode.getLink());
				linkItem.addActionListener(new ActionListener() {
					public void actionPerformed(ActionEvent e) {
						try {
							Desktop.getDesktop().browse(myURL);
						} catch (IOException e1) {
							LOGGER.warning("IO error " + e1.toString());
						}
					}
				});
				showMenu.add(linkItem);

				addSearchItems(showMenu, season.formatSeasonEp(episode.getNumber()), show.getTitle());
			}
		}
	}

	private void createNewShowsMenu(Menu newShows) {
		for(int i=0;i<ShowLibrary.getInstance().newShows.size();i++) {
			final Show show = ShowLibrary.getInstance().newShows.get(i);
			final String title = show.getTitle();
			final Menu showMenu = new Menu(title);
			newShows.add(showMenu);
			final Season season = show.getSeason();
			final Episode episode = season.getEpisode();
			String linkLabel = episode.getLinkLabel();
			String link = episode.getLink();
			//Add menu items add, remove, and link
			final MenuItem addItem = new MenuItem("Move to Shows");
			final MenuItem removeItem = new MenuItem("Remove & Ignore");
			final MenuItem linkItem = new MenuItem(linkLabel);
			showMenu.add(addItem);
			showMenu.add(removeItem);
			showMenu.add(linkItem);

			//Add menu item functionality
			addItem.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					Show newShow = ShowLibrary.getInstance().addShow(show);
					Season season = newShow.addSeason(1);
					Episode episode = show.findSeason(1).findEpisode(1);
					season.addEpisode(1, episode.getLink(), episode.getLinkLabel());
					ShowLibrary.getInstance().newShows.remove(show);
					ShowLibrary.getInstance().doUpdates();
					GUI.getInstance().refresh();
				}
			});

			//Link menu item functionality
			final URI myURL = URI.create(link);
			linkItem.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					try {
						Desktop.getDesktop().browse(myURL);
					} catch (IOException e1) {
						LOGGER.warning("IO error " + e1.toString());
					}
				}
			});

			addSearchItems(showMenu, "s01e01", show.getTitle());

			//Remove functionality
			removeItem.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					int result = JOptionPane.showConfirmDialog(null,"Are you sure you want to remove and ignore " + title, "Remove show " + title + "?", JOptionPane.YES_NO_OPTION);
					if(result==0) {
						showLib.removeNewShow(show);
						generatePopup();
					}
				}
			});
		}		
	}

	private void createAllShowsMenu(Menu allShows) {
		for(int i=0;i<ShowLibrary.getInstance().shows.size();i++) {
			final Show s1 = ShowLibrary.getInstance().shows.get(i);
			int numSeasons = s1.seasons.size();
			if(numSeasons>0) {
				final String title = s1.getTitle();
				final Menu showMenu = new Menu(title);
				allShows.add(showMenu);
				for(Season mySeason : s1.seasons) {
					final int mySeasonNum = mySeason.getNumber();
					int numEpisodes = mySeason.getEpisodes().size();

					for(Episode myEpisode : mySeason.getEpisodes()) {
						//Var setup
						final int myEpisodeNum = myEpisode.getNumber();
						final String myLink;
						final String myLabel;
						String formattedSeasonEp = mySeason.formatSeasonEp(myEpisodeNum);
						myLink = myEpisode.getLink();
						myLabel = myEpisode.getLinkLabel();

						//Menu setup
						MenuItem remove = new MenuItem("Remove");
						MenuItem linkItem = new MenuItem(myLabel);

						//Link menu item functionality
						if(!myLink.equals(" ")) {
							final URI myURL = URI.create(myLink);
							linkItem.addActionListener(new ActionListener() {
								public void actionPerformed(ActionEvent e) {
									try {
										Desktop.getDesktop().browse(myURL);
									} catch (IOException e1) {
										LOGGER.warning("IO error " + e1.toString());
									}
								}
							});
						}

						//Remove menu item functionality
						final Season season = mySeason;
						final Episode episode = myEpisode;
						remove.addActionListener(new ActionListener() {
							public void actionPerformed(ActionEvent e) {
								int result = JOptionPane.showConfirmDialog(null,"Are you sure you want to remove " + title + " season " + mySeasonNum + " episode " + myEpisodeNum, "Remove Episode?", JOptionPane.YES_NO_OPTION);
								if(result==0) {
									season.removeEpisode(episode);
									LOGGER.fine("Removed episode " + episode.getNumber() + " from " + title);
									if (season.getEpisodes().size()==0) {
										Show show = ShowLibrary.getInstance().getShow(title);
										show.removeSeason(season);
										LOGGER.fine("Season " + season.getNumber() + " of " + title + " is removed because it was empty.");
									}
									showLib.doUpdates();
								}
							}
						});

						//Algorithm to show the least number of nested menus
						Menu activeMenu = showMenu;
						Menu episodeMenu;
						if(numSeasons>1) {
							Menu seasonMenu = new Menu("Season " + mySeasonNum);
							episodeMenu = new Menu("Episode " + myEpisodeNum);
							activeMenu.add(seasonMenu);
							activeMenu = seasonMenu;
						}
						else {
							episodeMenu = new Menu("Episode (" + mySeasonNum + "x" + myEpisodeNum + ")");
						}
						if(numEpisodes>1) {
							activeMenu.add(episodeMenu);
							activeMenu = episodeMenu;
						}
						activeMenu.add(remove);
						if(!myLink.equals(" ")) { 
							activeMenu.add(linkItem);
						}
						addSearchItems(activeMenu, formattedSeasonEp, s1.getTitle());
						if(numSeasons==1 && numEpisodes==1) {
							showMenu.setLabel(title + " (" + mySeasonNum + "x" + myEpisodeNum + ")");
						}
					}
				}
			}
		}
	}

	//Iterates through searchURLs and adds them to the menu
	public void addSearchItems(Menu menu, String formattedSeasonEp, String title) {
		for(Search search : Settings.getInstance().getSearches()) {
			String label = search.getLabel();
			String url = search.getLink().replaceAll("#showtitle#", title.replaceAll("\\s", "+") + "+" + formattedSeasonEp);
			final URI uri = URI.create(url);
			MenuItem searchItem = new MenuItem(label);
			searchItem.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					try {
						Desktop.getDesktop().browse(uri);
					} catch (IOException ex) {
						LOGGER.warning("IO error " + ex.toString());
					}
				}
			});
			menu.add(searchItem);
		}
	}

	public void showMessages(ArrayList<String> pending) {
		final ArrayList<String> messages = new ArrayList<String>();
		messages.addAll(pending);
		Runnable showMessagesRunnable = new Runnable(){
			public void run() {
				for(int i=0;i<messages.size();i=i+5) {
					String message = "";
					for(int j=0;j<5 && j+i<messages.size();j++) {
						message = message + messages.get(i+j) + "\n";
					}
					trayIcon.displayMessage("Unify Update", message, TrayIcon.MessageType.INFO);
					try {
						Thread.sleep(5000);
					} catch (InterruptedException e) {
						LOGGER.severe(e.toString());
						System.exit(1);
					}
				}
			}
		};
		Thread newThread = new Thread(showMessagesRunnable);
		newThread.start();
	}

	public void exit(int i) {
		tray.remove(trayIcon);
		System.exit(i);
	}

}
